Passport.js 是一個使用者登入跟認證系統. 它有提供很多套件(strategy)給很多不同第三方認證 像facebook, google. 這個教學一樣會分成兩段, 第一段model篇, 第二段routes & view 篇
以下的gif 就是我們要做的功能.


// package.json
 ...
  "dependencies": {
    "bcryptjs": "^2.4.3",
    "body-parser": "~1.17.1",
    "connect-flash": "^0.1.1",
    "cookie-parser": "~1.4.3",
    "debug": "~2.6.3",
    "ejs": "~2.5.6",
    "express": "~4.15.2",
    "express-messages": "^1.0.1",
    "express-session": "^1.15.3",
    "express-validator": "^3.2.0",
    "mongoose": "^4.10.6",
    "morgan": "~1.8.1",
    "passport": "^0.3.2",
    "passport-local": "^1.0.0",
    "serve-favicon": "~2.4.2"
  }
// model/users.js
//先載入我們要的library
var mongoose = require('mongoose')
var bcrypt = require('bcryptjs')
//創造資料庫需要的欄位(schema)
var UserSchema = mongoose.Schema({
  username: { type: String, index: true},
  password: { type: String}
})
var User = module.exports = mongoose.model('User', UserSchema)
// 建立createUser方法, 然後用bcrypt加密 + 存檔
module.exports.createUser = function(newUser, callback) {
  bcrypt.genSalt(10, function(err, salt) {
    bcrypt.hash(newUser.password, salt, function(err, hash){
      newUser.password = hash
      newUser.save(callback)
    })
  })
}
// getUserByUsername, 用username來找使用者
module.exports.getUserByUsername = function(username, callback) {
  var query = { username: username }
  User.findOne(query, callback)
}
// getUserById, 用id來找使用者
module.exports.getUserById = function(id, callback) {
  User.findById(id, callback)
}
// comparePassword, 當使用者登入的時候我們要比對登入密碼跟我們資料庫密碼相同
module.exports.comparePassword = function(candidatePassword, hash,callback) {
  bcrypt.compare(candidatePassword, hash, function(err, isMatch) {
    if(err) throw err
    callback(null, isMatch)
});
}
var passport = require('passport')
var LocalStrategy = require('passport-local')
var mongoose = require('mongoose')
var flash = require('connect-flash');
var users = require('./routes/users');
...
mongoose.connect('mongodb://localhost/loginappv2');
var db = mongoose.connection;
...
app.use(passport.initialize());
app.use(passport.session());
// GET 登入頁面
router.get('/signin', function(req, res, next) {
  console.log(res.locals)
  res.render('signin');
});
// POST 登入頁面
router.post('/signin',
  passport.authenticate('local', {
    successRedirect: '/users/profile',
    failureRedirect: '/users/signin',
    failureFlash: true
  }),
  function(req, res) {
    res.redirect('/users/profile')
});
// GET 註冊頁面
router.get('/signup', function(req, res, next) {
  res.render('signup', {errors: ''});
});
// POST 註冊頁面
router.post('/signup', function(req, res, next) {
  // Parse Info
  var username = req.body.username
  var password = req.body.password
  //Create User
  var newUser = new User({
    username: username,
    password: password
  })
  User.createUser(newUser, function(err, user){
    if(err) throw err;
  })
  res.redirect('/users/signin')
});
// GET 登入後的 profile 頁面
// 這邊有用ensureAuthenticated 來看使用者是不是已登入過, 如果沒有就不可以來這一頁
router.get('/profile', ensureAuthenticated, function(req, res, next) {
  console.log(req.user)
  res.render('profile', {
    user: req.user.username
  });
});
// GET 登出
router.get('/logout', function(req, res, next) {
  req.logout()
  req.flash('success_msg', 'You are logged out')
  res.redirect('/users/signin')
})
module.exports = router;
function ensureAuthenticated(req, res, next){
  if(req.isAuthenticated()){
    return next();
  } else {
    req.flash('error_msg', 'you are not logged in')
    res.redirect('/users/signin')
  }
}
passport.use(new LocalStrategy(
  function(username, password, done) {
    User.findOne({ username: username }, function(err, user) {
      if (err) { return done(err); }
      if (!user) {
        return done(null, false, { message: 'Incorrect username.' });
      }
      User.comparePassword(password, user.password, function(err, isMatch){
        if(err) throw err
        if(isMatch) {
          return done(null, user)
        } else {
          return done(null, false, {message: 'Invalid password'})
        }
      })
    });
  }
));
passport.serializeUser(function(user, done) {
  done(null, user.id);
});
passport.deserializeUser(function(id, done) {
  User.getUserById(id, function(err, user) {
    done(err, user);
  });
});